package com.lkl.hystrixdemo.order.aspect;

import com.google.common.util.concurrent.RateLimiter;
import com.lkl.hystrixdemo.order.aspect.annotation.LklRateAnnotation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 定义切面，包装rateLimiter 限流技术
 */
@Aspect
@Component
public class LimitAspect {
    //存入URL 和 rateLimiter 的实列，保证 每个请求都是单列的
    private static Map<String, RateLimiter> rateMap = new ConcurrentHashMap();

    //可以是拦截执行方法或者可以通过注解拦截
    private final String POINT_CUT = "@annotation(com.lkl.hystrixdemo.order.aspect.annotation.LklRateAnnotation)";

    @Pointcut(POINT_CUT)
    public void pointCut(){}

    @Around("pointCut()")
    public Object run (ProceedingJoinPoint joinPoint) throws Throwable {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        LklRateAnnotation lklRateAnnotation = method.getAnnotation(LklRateAnnotation.class);
        //如果包含，限流的注解，判断限流策略
        if (lklRateAnnotation != null) {
            String url = getRequestURI();
            int maxPermits = lklRateAnnotation.value();
            int maxWaitTime = lklRateAnnotation.acquireTime();
            RateLimiter urlRateLimiter;
            if (rateMap.get(url) == null) {
                urlRateLimiter = RateLimiter.create(maxPermits);
                rateMap.put(url, urlRateLimiter);
            }else {
                urlRateLimiter = rateMap.get(url);
            }
            boolean isAcquire = urlRateLimiter.tryAcquire(maxWaitTime, TimeUnit.MICROSECONDS);
            if (!isAcquire) {
                return "限流啦";
            }
        }
        return joinPoint.proceed();
    }
    private String getRequestURI() {
        return getRequest().getRequestURI();
    }

    private HttpServletRequest getRequest() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return attributes.getRequest();
    }
}
